use crate::sym_type::*;
use crate::llvm::*;

impl Instr {
    pub fn fetch_info(&self) -> (InstrType, Vec<&str>, Vec<&SymType>) {
        match &self {
            Instr::Add(bin_op) => {
                let str_ty = bin_op.fetch_info();
                (InstrType::Add, str_ty.0, str_ty.1)
            },
            Instr::Sub(bin_op) => {
                let str_ty = bin_op.fetch_info();
                (InstrType::Sub, str_ty.0, str_ty.1)
            },
            Instr::Mul(bin_op) => {
                let str_ty = bin_op.fetch_info();
                (InstrType::Mul, str_ty.0, str_ty.1)
            },
            Instr::Sdiv(bin_op) => {
                let str_ty = bin_op.fetch_info();
                (InstrType::Sdiv, str_ty.0, str_ty.1)
            },
            Instr::Srem(bin_op) => {
                let str_ty = bin_op.fetch_info();
                (InstrType::Srem, str_ty.0, str_ty.1)
            },
            Instr::Fadd(bin_op) => {
                let str_ty = bin_op.fetch_info();
                (InstrType::Fadd, str_ty.0, str_ty.1)
            },
            Instr::Fsub(bin_op) => {
                let str_ty = bin_op.fetch_info();
                (InstrType::Fsub, str_ty.0, str_ty.1)
            },
            Instr::Fmul(bin_op) => {
                let str_ty = bin_op.fetch_info();
                (InstrType::Fmul, str_ty.0, str_ty.1)
            },
            Instr::Fdiv(bin_op) => {
                let str_ty = bin_op.fetch_info();
                (InstrType::Fdiv, str_ty.0, str_ty.1)
            },
            Instr::Zext(conver_op) => {
                let str_ty = conver_op.fetch_info();
                (InstrType::Zext, str_ty.0, str_ty.1)
            },
            Instr::Sitofp(conver_op) => {
                let str_ty = conver_op.fetch_info();
                (InstrType::Sitofp, str_ty.0, str_ty.1)
            },
            Instr::Fptosi(conver_op) => {
                let str_ty = conver_op.fetch_info();
                (InstrType::Fptosi, str_ty.0, str_ty.1)
            },
            Instr::Icmp(cond, bin_op) => {
                let (mut str_vec, ty_vec) = bin_op.fetch_info();
                str_vec.insert(0, cond.as_str());
                (InstrType::Icmp, str_vec, ty_vec)
            },
            Instr::Fcmp(cond, bin_op) => {
                let (mut str_vec, ty_vec) = bin_op.fetch_info();
                str_vec.insert(0, cond.as_str());
                (InstrType::Fcmp, str_vec, ty_vec)
            },
            Instr::Phi(res, ty, candidates) => {
                let mut str_vec = vec!(res.as_str());
                for (v, b) in candidates.iter() {
                    str_vec.push(v.as_str());
                    str_vec.push(b.as_str());
                }
                (InstrType::Phi, str_vec, vec!(&ty))
            },
            Instr::Alloca{result, ty, len} => {
                (InstrType::Alloca, vec!(result.as_str(), len.as_str()), vec!(&ty))
            },
            Instr::Store{ty, value, ptr, len} => {
                (InstrType::Store, vec!(value.as_str(), ptr.as_str(), len.as_str()), vec!(&ty))
            },
            Instr::Load{result, ty, ptr, len} => {
                (InstrType::Load, vec!(result.as_str(), ptr.as_str(), len.as_str()), vec!(&ty))
            },
            Instr::Call(res, label, ty, params) => {
                let mut str_vec = vec!(res.as_str(), label.as_str());
                for (v, _) in params.iter() {
                    str_vec.push(v.as_str());
                }
                let mut ty_vec = vec!(ty);
                for (_, t) in params.iter() {
                    ty_vec.push(t);
                }
                (InstrType::Call, str_vec, ty_vec)
            },
            Instr::GetElemPtr(dst, ty, ptr, idx) => {
                let mut str_vec = vec!(dst.as_str(), ptr.as_str());
                for i in idx.iter() {
                    str_vec.push(i.as_str());
                }
                (InstrType::GetElemPtr, str_vec, vec!(&ty))
            },
            Instr::BitCast(res, ty, val, ty2) => {
                (InstrType::BitCast, vec!(res.as_str(), val.as_str()), vec!(&ty, &ty2))
            },
            Instr::Comment(content) => {
                (InstrType::Comment, vec!(content.as_str()), vec!())
            },
            Instr::Return(ty, val) => {
                if let Some(v) = val {
                    (InstrType::Return, vec!(v.as_str()), vec!(&ty))
                } else {
                    (InstrType::Return, vec!(), vec!(&ty))
                }
            },
            Instr::Branch(cond, label1, label2) => {
                if let (Some(c), Some(l2)) = (cond, label2) {
                    (InstrType::Branch, vec!(c.as_str(), label1.as_str(), l2.as_str()), vec!())
                } else {
                    (InstrType::Branch, vec!("", label1.as_str(), ""), vec!())
                }
            },
        }
    }
}

impl BinaryOp {
    fn fetch_info(&self) -> (Vec<&str>, Vec<&SymType>) {
        (vec!(self.result.as_str(), self.op1.as_str(), self.op2.as_str()), vec!(&self.ty))
    }
} 

impl ConverOp {
    fn fetch_info(&self) -> (Vec<&str>, Vec<&SymType>) {
        (vec!(self.result.as_str(), self.value.as_str()), vec!(&self.ty, &self.ty2))
    }
} 

